/*
 * Copyright 2009 Colin Prinn
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 */

package net.recursv.motific.at.runner;

import net.recursv.motific.at.AtException;
import net.recursv.motific.at.command.AtCommand;
import net.recursv.motific.at.impl.__MIDlet;
import net.recursv.motific.at.scene.GraphicsWrapper;
import net.recursv.motific.at.scene.SceneManager;
import net.recursv.motific.utils.Nullable;

import javax.microedition.lcdui.Graphics;
import java.util.Vector;

/**
 * Created by: recursv
 * Date: 9/08/2009
 * Time: 7:36:25 AM
 */
public class AtScript implements Nullable {
    private Vector _commands;
    private AtScriptObserver _observer;
    private int _index = 0;
    private String _title;
    private __MIDlet _target;
    public static long TIMEOUT = 5000;

    public AtScript(String title) {
        _title = title;
        _commands = new Vector();
    }

    public void paint(Graphics g) {
        GraphicsWrapper wrapper = SceneManager.lockWrapper(g);
        _target.paint(wrapper);
        SceneManager.unlockWrapper();
    }

    public void setup(AtScriptObserver observer, __MIDlet target) {
        _observer = observer;
        _target = target;
    }

    public void tearDown() {
        _observer = null;
        _commands = null;
        _target = null;
    }

    public void execute() {
        if (_commands.size() > _index)
            runCommand((AtCommand) _commands.elementAt(_index));
        else
            _observer.notifyComplete(this);
    }

    private void runCommand(AtCommand command) {
        command.setTarget(_target);
        try {
            long start = System.currentTimeMillis();
            command.execute();
            if (TIMEOUT == -1 || System.currentTimeMillis() - start < TIMEOUT)
                passCommand(command);
            else
                failCommandOnTime(command);
        } catch (Throwable throwable) {
            failCommand(command, throwable);
        }
    }

    private void passCommand(AtCommand command) {
        finaliseCommand(command, false);

        _observer.notifySuccess(command);

        _index++;
        execute();
    }

    /**
     * @param command to fail
     * @noinspection ThrowableInstanceNeverThrown
     */
    private void failCommandOnTime(AtCommand command) {
        failCommand(command, new AtException("Line " + command.getLineNumber() + " in script " + getTitle() + ", The command " + command.getTitle() + " timed out"));
    }

    private void failCommand(AtCommand command, Throwable timeoutException) {
        finaliseCommand(command, true);

        _observer.notifyFailed(command, timeoutException);
        _observer.notifyComplete(this);
    }

    private void finaliseCommand(AtCommand command, boolean failed) {
        command.setTarget(null);
        command.isFailed(failed);
    }

    public boolean isNull() {
        return false;
    }

    public String getTitle() {
        return _title;
    }

    public void addCommand(AtCommand atCommand) {
        _commands.addElement(atCommand);
    }

    public static final AtScript NULL = new AtScript("NULL") {
        public void paint(Graphics g) {
        }

        public void setup(AtScriptObserver observer, __MIDlet target) {
        }

        public void tearDown() {
        }

        public void execute() {
        }

        public boolean isNull() {
            return true;
        }

        public void addCommand(AtCommand atCommand) {
        }

        public String toString() {
            return "Null At Script";
        }
    };
}
